﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NPOI.HSSF.UserModel;
using NPOI.OpenXmlFormats.Spreadsheet;
using NPOI.SS.UserModel;
using NPOI.XSSF.Streaming;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Security.Policy;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Web;

namespace Delphi翻译
{
    public class WordTranslator : IDisposable
    {
        IWorkbook workbook;
        string TKK;
        string FileName;
        public WordTranslator(string filename)
        {
            if (!File.Exists(filename))
            {
                throw new FileNotFoundException();
            }
            //var bbbb = wo("· Connect", "334339.250873");
            //TKK = GetTkk();
            //if (TKK == null)
            //{
            //    throw new Exception("无法获取tkk");
            //}
            FileName = filename;
        }

        public void Dispose()
        {
            //if (reader != null)
            //{
            //    reader.Close();
            //}
        }

        const string BaseUrl = "https://translate.google.cn";
        bool cancel = false;
        public void DoTranslte()
        {
            workbook = WorkbookFactory.Create(FileName);
            var worksheet = workbook.GetSheetAt(0);
            cancel = false;
            int all = worksheet.LastRowNum;
            foreach (XSSFRow row in worksheet)
            {
                if (row.Cells.Count > 1)
                {
                    if (row.Cells[1].StringCellValue != "")
                        continue;
                }
                Debug.WriteLine($"第{row.RowNum}/{all}个");
                var cell = row.Cells[0];
                cell.SetCellType(CellType.String);
                var y = cell.StringCellValue;
                Debug.WriteLine($"原始：{y}");

                int retryTime = 2;
            retry:
                if (cancel)
                {
                    Debug.WriteLine($"已取消");
                    break;
                }
                var result = OfficeDoTranslate(y);
                if (result == null)
                {
                    if (retryTime-- > 0)
                    {
                        Debug.WriteLine($"重试{10 - retryTime}次");
                        Thread.Sleep(50);
                        goto retry;
                    }
                    else
                        continue;
                }
                try
                {
                    var fy = result;
                    Debug.WriteLine($"翻译：{fy}");
                    row.CreateCell(1).SetCellValue(fy);
                }
                catch { }

                //Thread.Sleep(500); 
            }

            using (var fs = new FileStream(FileName, FileMode.Create))
            {
                workbook.Write(fs);
            }
        }

        string GetCellValue(ICell cell)
        {
            switch (cell.CellType)
            {
                case CellType.Blank:
                    break;
                case CellType.Boolean:
                    return cell.BooleanCellValue.ToString();
            }
            cell.ToString();
            return "";
        }

        public string GetTkk()
        {
            var abcd = DoGetRequestSendData(BaseUrl);
            var match = Regex.Match(abcd, @"tkk:'(\d+\.\d+)'");
            if (match.Success)
            {
                return match.Groups[1].Value;
            }
            return null;
        }

        /// <summary>
        /// 无符号右移，与JS中的>>>等价
        /// </summary>
        /// <param name="x">要移位的数</param>
        /// <param name="y">移位数</param>
        /// <returns></returns>
        public static int UIntMoveRight(int x, int y)
        {
            int mask = 0x7fffffff; //Integer.MAX_VALUE
            for (int i = 0; i < y; i++)
            {
                x >>= 1;
                x &= mask;
            }
            return x;
        }

        int uo(int a, string b)
        {
            for (var c = 0; c < b.Length - 2; c += 3)
            {
                int d = b[c + 2];
                d = 'a' <= d ? d - 87 : int.Parse(new string((char)d, 1));
                d = '+' == b[c + 1] ? UIntMoveRight(a, d) : a << d;
                a = '+' == b[c] ? a + d & -1 : a ^ d;
            }
            return a;
        }

        string wo(string a, string tkk)
        {
            var d = tkk.Split('.');
            var b = int.Parse(d[0]);
            var f = 0;
            List<int> e = new List<int>(255);
            for (var g = 0; g < a.Length; g++)
            {
                int h = a[g];
                if (128 > h)
                {
                    e.Add(h);
                }
                else
                {
                    if (2048 > h)
                    {
                        e.Add(h >> 6 | 192);
                        e.Add(h & 63 | 128);
                    }
                    else
                    {
                        if (55296 == (h & 64512) && g + 1 < a.Length && 56320 == ((int)a[g + 1] & 64512))
                        {
                            h = 65536 + ((h & 1023) << 10) + (a[++g] & 1023);
                            e.Add(h >> 18 | 240);
                            e.Add(h >> 12 & 63 | 128);
                        }
                        else
                        {
                            e.Add(h >> 12 | 224);
                            e.Add(h >> 6 & 63 | 128);
                        }
                        e.Add(h & 63 | 128);
                    }
                }
            }
            var aa = b;
            for (f = 0; f < e.Count; f++)
            {
                aa += e[f];
                aa = uo(aa, "+-a^+6");
            }
            aa = uo(aa, "+-3^+b+-f");
            aa = aa ^ (int)uint.Parse(d[1]);
            uint ars = (uint)aa;
            ars = ars % 1000000;
            return (ars.ToString() + "." + (ars ^ b));

        }


        private static string GetResponseBody(HttpWebResponse response)

        {
            string responseBody = string.Empty;
            if (response.ContentEncoding.ToLower().Contains("gzip"))
            {
                using (GZipStream stream = new GZipStream(response.GetResponseStream(), CompressionMode.Decompress))
                {
                    using (StreamReader reader = new StreamReader(stream))
                    {
                        responseBody = reader.ReadToEnd();
                    }
                }
            }
            else if (response.ContentEncoding.ToLower().Contains("deflate"))
            {
                using (DeflateStream stream = new DeflateStream(response.GetResponseStream(), CompressionMode.Decompress))
                {
                    using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
                    {
                        responseBody = reader.ReadToEnd();
                    }
                }
            }
            else
            {
                using (Stream stream = response.GetResponseStream())
                {
                    using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
                    {
                        responseBody = reader.ReadToEnd();
                    }
                }
            }
            return responseBody;

        }

        public static string OfficeDoTranslate(string word)
        {
            try
            {
                var hwRequest = (HttpWebRequest)WebRequest.Create("https://ogma.osi.office.net/TradukoApi/api/v1.0/translateContent?fromLang=en&toLang=zh-chs");
                hwRequest.Timeout = 2000;
                hwRequest.Accept = "application/json, text/plain, */*";
                hwRequest.ContentType = "application/json";
                hwRequest.Headers["x-translator-sid"] = "fef8d0b4-2fc1-4258-b9a3-637f281d8667";
                hwRequest.Headers["x-translator-cross-sid"] = "729c5a5a-ad1a-48b4-9f4a-c0e58b4fc557";
                hwRequest.Headers["x-translator-cid"] = "905f6a67-a115-44ac-b667-9b78d70dc85a";
                hwRequest.Headers["x-translator-client-version"] = "v1.0";
                hwRequest.Headers["x-translator-client-app"] = "Excel";
                hwRequest.Headers["x-translator-client-type"] = "Win32";
                hwRequest.Headers["x-translator-trigger-type"] = "Manual";
                hwRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36";
                hwRequest.Referer = "https://ogma.osi.office.net/TradukoApi/UI/index.html?_host_Info=Excel$Win32$16.01$zh-CN$$$$0";
                hwRequest.Method = "POST";
                var stream = hwRequest.GetRequestStream();
                word = word.Replace("\"", "\\\\\\\"");
                var json = $@"{{""Data"":""{word}"",""DataType"":""Text""}}";
                var bs = Encoding.UTF8.GetBytes(json);
                stream.Write(bs, 0, bs.Length);
                stream.Flush();
                HttpWebResponse hwResponse = (HttpWebResponse)hwRequest.GetResponse();
                var strResult = GetResponseBody(hwResponse);
                var jobj = JsonConvert.DeserializeObject(HttpUtility.UrlDecode(strResult)) as JObject;
                strResult = jobj["TranslatedText"].ToString();
                strResult = strResult.Replace("。", ".");
                strResult = strResult.Replace("，", ",");
                strResult = strResult.Replace("“", "\"");
                strResult = strResult.Replace("”", "\"");
                strResult = strResult.Replace("（", "(");
                strResult = strResult.Replace("）", ")");
                strResult = strResult.Replace("｛", "{");
                strResult = strResult.Replace("｝", "}");
                hwResponse.Close();
                return strResult;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        /// <summary>
        /// GET方式发送得结果
        /// </summary>
        /// <param name="url">请求的url</param>
        public static string DoGetRequestSendData(string url)
        {
            string strResult = string.Empty;
            try
            {
                HttpWebRequest hwRequest = (HttpWebRequest)WebRequest.Create(url);
                hwRequest.Timeout = 2000;
                hwRequest.Accept = "*/*";
                hwRequest.Headers["accept-encoding"] = "gzip, deflate, br";
                hwRequest.Headers["accept-language"] = "zh-CN,zh;q=0.9";
                hwRequest.Headers["sec-fetch-dest"] = "empty";
                hwRequest.Headers["sec-fetch-mode"] = "cors";
                hwRequest.Headers["sec-fetch-site"] = "same-origin";
                hwRequest.Headers["x-client-data"] = "CIq2yQEIorbJAQjBtskBCKmdygEImbXKAQifuMoBCPXHygEI58jKAQjpyMoBCLTLygEI3c7KAQjC18oBCJfYygEIn9jKAQ==";
                hwRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36";
                hwRequest.Referer = "https://translate.google.cn/";
                hwRequest.Method = "GET";
                hwRequest.CookieContainer = new CookieContainer();
                hwRequest.CookieContainer.Add(new Cookie("_ga", "GA1.3.2054735122.1594363482", "/", ".translate.google.cn"));
                hwRequest.CookieContainer.Add(new Cookie("NID", "204=tvMTMHZkgA5HmYYTZYPFwtj9BQXByjbcu3RH8njfeqR3zkyPPii_lqgAueVwKLZRWKJZR_Q8p3bIvm9Q2bAcuVFv8POfX4NUGWx-5i3NeHpM-McsHVmm_WqgFJzrE_UJLjrytGiIxgbRvlUyVOhn69q93Tkp9BzesT6QBkshTQ4", "/", ".google.cn"));
                hwRequest.CookieContainer.Add(new Cookie("_gid", "GA1.3.1539643743.1601613525", "/", ".translate.google.cn"));
                HttpWebResponse hwResponse = (HttpWebResponse)hwRequest.GetResponse();
                strResult = GetResponseBody(hwResponse);
                hwResponse.Close();
            }
            catch (Exception)
            {
            }
            return strResult;
        }

        internal void Cancel()
        {
            cancel = true;
        }
    }

}
